/*
 * This file is part of b.AMP.
 *
 * b.AMP is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * b.AMP is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with b.AMP.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __BAMP_MESSAGE_ACK_HPP__
#define __BAMP_MESSAGE_ACK_HPP__

#include <tr1/functional>
#include <boost/thread.hpp>
#include <boost/interprocess/sync/interprocess_semaphore.hpp>


namespace bamp
{

    typedef std::tr1::function<void()> MessageAckFunc;


    /**
     * Message acknowledgement. This kind of object is returned
     * whenever an message has been sent. Its main use is to
     * synchronize threads by waiting until a message has finished
     * being processed by all handlers (see wait()).
     */
    class MessageAck {

    public:

        /**
         * Constructor. Initializes the acknowledgement object with
         * the number of message handlers.
         * @param number of message handlers
         */
        explicit MessageAck(const int count = 0);

        /**
         * Destructor.
         */
        ~MessageAck();

        /**
         * Copy constructor. Increases the reference count.
         * @param e object to copy
         */
        MessageAck(const MessageAck& e);

        /**
         * Copy assignment operator. Increases the reference count.
         * @param e object to copy
         * @return  this
         */
        const MessageAck& operator=(const MessageAck& e);

        /**
         * Wait until the message that generated this object has
         * finished being handled by all its handlers on the channel
         * it was dispatched on.
         */
        void wait();

        /**
         * Configure a callback to call after the message has finished
         * being handled. If the message has already finished being
         * handled, the funtion is called immediately.
         */
        void callback(const MessageAckFunc& function);

        /**
         * Acknowledge that one more handler has finished. All
         * handlers must call this exactly once for calls to wait() to
         * stop blocking.
         */
        void ack();

    private:

        typedef boost::interprocess::interprocess_semaphore Semaphore;

        Semaphore*      m_handlerSem;    /// Handler countdown semaphore
        Semaphore*      m_waitSem;       /// Semaphore for non-blocking wait()
        Semaphore*      m_refSem;        /// Reference counting semaphore
        MessageAckFunc* m_function;      /// Callback
        boost::mutex    m_functionMutex; /// Mutex for m_function
    };

} // namespace bamp

#endif // __BAMP_MESSAGE_ACK__
